﻿#pragma once

#include "geom_surface.h"
#include "kernel/spline.h"

namespace CAGD
{

/**
 * B 样条曲面.
 */
class GeomBSplineSurface : public GeomSurface
{
  public:
    using pnt_array = std::vector<BPnt>;
    using pnt_array_2d = std::vector<pnt_array>;
    using pnt_array_3d = std::vector<pnt_array_2d>;
    using pnt_array_4d = std::vector<pnt_array_3d>;

    /**
     * @brief 次数 + 控制点构造.
     * @param udeg
     * @param vdeg
     * @param cpts
     * @see
     */
    GeomBSplineSurface(int udeg, int vdeg, const std::vector<std::vector<BPnt>> &cpts);

    /**
     * @brief 节点 + 控制点构造.
     * @param uknots
     * @param vknots
     * @param cpts
     * @see
     */
    GeomBSplineSurface(const std::vector<double> &uknots, const std::vector<double> &vknots,
                       const std::vector<std::vector<BPnt>> &cpts);

    /**
     * @brief u 节点向量长度.
     * @return
     * @see
     */
    int NbUKnots() const;

    /**
     * @brief u 节点向量.
     * @return
     * @see
     */
    const std::vector<double> &UKnots() const;

    /**
     * @brief 设置 u 节点向量.
     * @param knots
     * @see
     */
    void SetUKnots(const std::vector<double> &knots);

    /**
     * @brief v 节点向量长度.
     * @return
     * @see
     */
    int NbVKnots() const;

    /**
     * @brief v 节点向量.
     * @return
     * @see
     */
    const std::vector<double> &VKnots() const;

    /**
     * @brief 设置 v 节点向量.
     * @param knots
     * @see
     */
    void SetVKnots(const std::vector<double> &knots);

    /**
     * @brief 控制点数组.
     * @return
     * @see
     */
    const std::vector<std::vector<BPnt>> &Poles() const;

    /**
     * @brief 设置控制点数组.
     * @param cpts
     * @see
     */
    void SetPoles(const std::vector<std::vector<BPnt>> &cpts);

    /**
     * @brief u 次数.
     * @return
     * @see
     */
    int UDegree() const;

    /**
     * @brief v 次数.
     * @return
     * @see
     */
    int VDegree() const;

    /**
     * @brief 获得 d 阶导后的控制点.
     * @return
     * @param d
     * @param r1
     * @param r2
     * @param s1
     * @param s2
     * @see
     */
    pnt_array_4d DerivCpts(int d, int r1, int r2, int s1, int s2) const;

    /**
     * @brief 计算不高于 d 阶导的值.
     * @return
     * @param d
     * @param u
     * @param v
     * @see
     */
    pnt_array_2d DerivPoints(int d, double u, double v) const;

    /**
     * @brief 获得参数范围.
     * @param U1
     * @param U2
     * @param V1
     * @param V2
     * @see
     */
    void Bounds(double &U1, double &U2, double &V1, double &V2) const override;

    /**
     * @brief 取值函数.
     * @param u
     * @param v
     * @param P
     * @see
     */
    void D0(double u, double v, BPnt &P) const override;

    /**
     * @brief u,v 切向函数.
     * @param u
     * @param v
     * @param P
     * @param DU
     * @param DV
     * @see
     */
    void D1(double u, double v, BPnt &P, BVec &DU, BVec &DV) const override;

    /**
     * @brief 2 阶导函数.
     * @param u
     * @param v
     * @param P
     * @param DU
     * @param DV
     * @param DUU
     * @param DVV
     * @param DUV
     * @see
     */
    void D2(double u, double v, BPnt &P, BVec &DU, BVec &DV, BVec &DUU, BVec &DVV, BVec &DUV) const override;

    /**
     * @brief n 阶导函数.
     * @param N
     * @param u
     * @param v
     * @param V
     * @see
     */
    void DN(int N, double u, double v, std::vector<std::vector<BPnt>> &V) const override;

    /**
     * @brief 获得 u 等参线节点.
     * @return
     * @see
     */
    std::vector<double> IsoUKnots() const override;

    /**
     * @brief 获得 v 等参线节点.
     * @return
     * @see
     */
    std::vector<double> IsoVKnots() const override;

    /**
     * @brief u 等参线数量.
     * @return
     * @see
     */
    int NbIsoU() const override;

    /**
     * @brief v 等参线数量.
     * @return
     * @see
     */
    int NbIsoV() const override;

    /**
     * @brief 引用控制网格.
     * @return
     * @see
     */
    std::vector<BPnt *> MakeReference() override;

  protected:
    std::vector<double> m_uknots;
    std::vector<double> m_vknots;
    std::vector<std::vector<BPnt>> m_cpts;
};

} // namespace CAGD
